
#include "exynos_4412.h"

/****************MPU6050内部寄存器地址****************/

#define	SMPLRT_DIV		0x19	//陀螺仪采样率，典型值：0x07(125Hz)
#define	CONFIG			0x1A	//低通滤波频率，典型值：0x06(5Hz)
#define	GYRO_CONFIG		0x1B	//陀螺仪自检及测量范围，典型值：0x18(不自检，2000deg/s)
#define	ACCEL_CONFIG	0x1C	//加速计自检、测量范围及高通滤波频率，典型值：0x18(不自检，2G，5Hz)
#define	ACCEL_XOUT_H	0x3B
#define	ACCEL_XOUT_L	0x3C
#define	ACCEL_YOUT_H	0x3D
#define	ACCEL_YOUT_L	0x3E
#define	ACCEL_ZOUT_H	0x3F
#define	ACCEL_ZOUT_L	0x40
#define	TEMP_OUT_H		0x41
#define	TEMP_OUT_L		0x42
#define	GYRO_XOUT_H		0x43
#define	GYRO_XOUT_L		0x44
#define	GYRO_YOUT_H		0x45
#define	GYRO_YOUT_L		0x46
#define	GYRO_ZOUT_H		0x47
#define	GYRO_ZOUT_L		0x48
#define	PWR_MGMT_1		0x6B	//电源管理，典型值：0x00(正常启用)
#define	WHO_AM_I		0x75	//IIC地址寄存器(默认数值0x68，只读)
#define	SlaveAddress	0x68	//MPU6050-I2C地址

//警报状态下，是否让蜂鸣器响，默认是响起
int flag = 1;

/************************延时函数************************/

void mydelay_ms(int time)
{
	int i,j;
	while(time--)
	{
		for(i=0;i<5;i++)
			for(j=0;j<514;j++);
	}
}

/**********************************************************************
 * 函数功能：I2C向特定地址写一个字节
 * 输入参数：
 * 		slave_addr： I2C从机地址
 * 			  addr： 芯片内部特定地址
 * 			  data：写入的数据
 **********************************************************************/

void iic_write (unsigned char slave_addr, unsigned char addr, unsigned char data)
{
	/*对时钟源进行512倍预分频  打开IIC中断（每次完成一个字节的收发后中断标志位会自动置位）*/
	I2C5.I2CCON = I2C5.I2CCON | (1<<6) | (1<<5);

	/*设置IIC模式为主机发送模式  使能IIC发送和接收*/
	I2C5.I2CSTAT = 0xd0;
	/*将第一个字节的数据写入发送寄存器  即从机地址和读写位（MPU6050-I2C地址+写位0）*/
	I2C5.I2CDS = slave_addr<<1;
	/*设置IIC模式为主机发送模式  发送起始信号启用总线  使能IIC发送和接收*/
	I2C5.I2CSTAT = 0xf0;

	/*等待从机接受完一个字节后产生应答信号（应答后中断挂起位自动置位）*/
	while(!(I2C5.I2CCON & (1<<4)));

	/*将要发送的第二个字节数据（即MPU6050内部寄存器的地址）写入发送寄存器*/
	I2C5.I2CDS = addr;
	/*清除中断挂起标志位  开始下一个字节的发送*/
	I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
	/*等待从机接受完一个字节后产生应答信号（应答后中断挂起位自动置位）*/
	while(!(I2C5.I2CCON & (1<<4)));

	/*将要发送的第三个字节数据（即要写入到MPU6050内部指定的寄存器中的数据）写入发送寄存器*/
	I2C5.I2CDS = data;
	/*清除中断挂起标志位  开始下一个字节的发送*/
	I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
	/*等待从机接受完一个字节后产生应答信号（应答后中断挂起位自动置位）*/
	while(!(I2C5.I2CCON & (1<<4)));

	/*发送停止信号  结束本次通信*/
	I2C5.I2CSTAT = 0xD0;
	/*清除中断挂起标志位*/
	I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
	/*延时*/
	mydelay_ms(10);
}

/**********************************************************************
 * 函数功能：I2C从特定地址读取1个字节的数据
 * 输入参数：         slave_addr： I2C从机地址
 * 			       addr： 芯片内部特定地址
 * 返回参数： unsigned char： 读取的数值
 **********************************************************************/

unsigned char iic_read(unsigned char slave_addr, unsigned char addr)
{

	unsigned char data = 0;

	/*对时钟源进行512倍预分频  打开IIC中断（每次完成一个字节的收发后中断标志位会自动置位）*/
	I2C5.I2CCON = I2C5.I2CCON | (1<<6) | (1<<5);

	/*设置IIC模式为主机发送模式  使能IIC发送和接收*/
	I2C5.I2CSTAT = 0xd0;
	/*将第一个字节的数据写入发送寄存器  即从机地址和读写位（MPU6050-I2C地址+写位0）*/
	I2C5.I2CDS = slave_addr<<1;
	/*设置IIC模式为主机发送模式  发送起始信号启用总线  使能IIC发送和接收*/
	I2C5.I2CSTAT = 0xf0;
	/*等待从机接受完一个字节后产生应答信号（应答后中断挂起位自动置位）*/
	while(!(I2C5.I2CCON & (1<<4)));

	/*将要发送的第二个字节数据（即要读取的MPU6050内部寄存器的地址）写入发送寄存器*/
	I2C5.I2CDS = addr;
	/*清除中断挂起标志位  开始下一个字节的发送*/
	I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
	/*等待从机接受完一个字节后产生应答信号（应答后中断挂起位自动置位）*/
	while(!(I2C5.I2CCON & (1<<4)));

	/*清除中断挂起标志位  重新开始一次通信  改变数据传送方向*/
	I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));

	/*将第一个字节的数据写入发送寄存器  即从机地址和读写位（MPU6050-I2C地址+读位1）*/
	I2C5.I2CDS = slave_addr << 1 | 0x01;
	/*设置IIC为主机接收模式  发送起始信号  使能IIC收发*/
	I2C5.I2CSTAT = 0xb0;
	/*等待从机接收到数据后应答*/
	while(!(I2C5.I2CCON & (1<<4)));


	/*禁止主机应答信号（即开启非应答  因为只接收一个字节）  清除中断标志位*/
	I2C5.I2CCON = I2C5.I2CCON & (~(1<<7))&(~(1<<4));
	/*等待接收从机发来的数据*/
	while(!(I2C5.I2CCON & (1<<4)));
	/*将从机发来的数据读取*/
	data = I2C5.I2CDS;

	/*直接发起停止信号结束本次通信*/
	I2C5.I2CSTAT = 0x90;
	/*清除中断挂起标志位*/
	I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
	/*延时等待停止信号稳定*/
	mydelay_ms(10);

	return data;

}


/**********************************************************************
 * 函数功能：MPU6050初始化
 **********************************************************************/

void MPU6050_Init ()
{
	iic_write(SlaveAddress, PWR_MGMT_1, 0x00); 		//设置使用内部时钟8M
	iic_write(SlaveAddress, SMPLRT_DIV, 0x07);		//设置陀螺仪采样率
	iic_write(SlaveAddress, CONFIG, 0x06);			//设置数字低通滤波器
	iic_write(SlaveAddress, GYRO_CONFIG, 0x18);		//设置陀螺仪量程+-2000度/s
	iic_write(SlaveAddress, ACCEL_CONFIG, 0x0);		//设置加速度量程+-2g
}

/**********************************************************************
 * 函数功能：初始化RTC
 **********************************************************************/
void RTC_init(){
	/*使能RTC控制*/
	RTCCON = RTCCON | 1;
	/*校准时间信息 表达初始时间：2023-12-31 星期日 23:59:50 这里4412的周几和当日字段写反了*/
	RTC.BCDYEAR = 0x023;
	RTC.BCDMON  = 0x12;
	RTC.BCDDAY  = 0x7;
	RTC.BCDWEEK = 0x31;
	RTC.BCDHOUR = 0x23;
	RTC.BCDMIN  = 0x59;
	RTC.BCDSEC  = 0x50;
	/*禁止RTC控制*/
	RTCCON = RTCCON &  (~(1));
}

/**********************************************************************
 * 函数功能：初始化PWM
 **********************************************************************/
void PWM_init(){
	/*1.将GPD0_0引脚设置成PWM0的输出引脚*/
	GPD0.CON = GPD0.CON & (~(0xF)) | (0x2);
	/*2.设置PWM0的一级分频	一级分频倍数设置为100倍*/
	PWM.TCFG0 = PWM.TCFG0 & (~(0xFF)) | 99;
	/*2.设置PWM0的二级分频	二级分频倍数设置为1倍  递减计数器递减频率 = PLCK / (99 + 1) / 1 = 1M*/
	PWM.TCFG1 = PWM.TCFG1 & (~(0xF));
	/*4.设置PWM0为自动重装载，使其能够产生连续的脉冲信号*/
	PWM.TCON = PWM.TCON | (1 << 3);
	/*5.设置PWM0的频率为1000HZ*/
	PWM.TCNTB0 = 1000;
	/*6.设置PWM0的占空比为60%*/
	PWM.TCMPB0 = 600;
	/*7.将TCNTB0中的值手动装载到递减计数器*/
	PWM.TCON = PWM.TCON | (1 << 1);
	/*8.关闭手动更新*/
	PWM.TCON = PWM.TCON & (~(1 << 1));
	PWM.TCON = PWM.TCON & (~(1));
}
void Delay(unsigned int Time)
{
	while(Time--);
}

//IRQ异常处理
void do_irq(void)
{
	unsigned int IrqNum = 0;
	/*从中断控制器中获取当前中断的中断号*/
	IrqNum = CPU0.ICCIAR & 0x3FF;

	/*根据中断号处理不同的中断*/
	switch(IrqNum)
	{
	case 0:
		//0号中断的处理程序
		break;
	case 1:
		//1号中断的处理程序
		break;
		/*
		 * ... ...
		 */
	case 57:
		//当k2按下时，停止蜂鸣
		flag = 0;
		/*清除GPIO控制器中GPX1_1的中断挂起标志位*/
		EXT_INT41_PEND = (1 << 1);
		/*将当前中断的中断号写回到中断控制器中，以这种方式来告知中断控制器当前的中断已经处理完成，可以发送其它中断*/
		CPU0.ICCEOIR = CPU0.ICCEOIR & (~(0x3FF)) | (57);
		break;
		/*
		 * ... ...
		 */
	case 159:
		//159号中断的处理程序
		break;
	default:
		break;
	}

}

void Interupt_init(){
	/*外设层次 - 让外部的硬件控制器产生一个中断信号发送给中断控制器*/
	/*将GPX1_1设置成中断功能*/
	GPX1.CON = GPX1.CON | (0xF << 4);
	/*设置GPX1_1的中断触发方式为下降沿触发*/
	EXT_INT41_CON = EXT_INT41_CON & (~(0x7 << 4)) | (0x2 << 4);
	/*使能GPX1_1的中断功能*/
	EXT_INT41_MASK = EXT_INT41_MASK & (~(1 << 1));

	/*中断控制器层次 - 让中断控制器接收外设产生的中断信号并对其进行管理然后再转发给CPU处理*/
	/*全局使能中断控制器使其能接收外设产生的中断信号并转发到CPU接口*/
	ICDDCR = ICDDCR | 1;
	/*在中断控制器中使能57号中断，使中断控制器接收到57号中断后能将其转发到CPU接口*/
	ICDISER.ICDISER1 = ICDISER.ICDISER1 | (1 << 25);
	/*选择由CPU0来处理57号中断*/
	ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xFF << 8)) | (0X01 << 8);
	/*使能中断控制器和CPU0之间的接口，使中断控制器转发的中断信号能够到达CPU0*/
	CPU0.ICCICR = CPU0.ICCICR | 1;

	GPX2.CON = GPX2.CON & (~(0xF << 28)) | (0x1 << 28);
}

/**********************************************************************
 * 函数功能：主函数
 **********************************************************************/

int main(void)
{

	unsigned char xvalue_h,xvalue_l;						//存储读取结果
	short int xvalue;
	unsigned int OldSec = 0, NewSec = 0;
	unsigned int OldMin = 0, NewMin = 0;



	/*设置GPB_2引脚和GPB_3引脚功能为I2C传输引脚*/
	GPB.CON = (GPB.CON & ~(0xF<<12)) | 0x3<<12;			 	//设置GPB_3引脚功能为I2C_5_SCL
	GPB.CON = (GPB.CON & ~(0xF<<8))  | 0x3<<8;				//设置GPB_2引脚功能为I2C_5_SDA

	uart_init(); 											//初始化串口
	MPU6050_Init();											//初始化MPU6050
	RTC_init();												//初始化RTC
	Interupt_init();									    //初始化中断
	
	printf("\n********** I2C test!! ***********\n");
	while(1)
	{
		xvalue_h = iic_read(SlaveAddress, ACCEL_XOUT_H);		//获取MPU6050-X轴加速度高字节
		xvalue_l = iic_read(SlaveAddress, ACCEL_XOUT_L);		//获取MPU6050-X轴加速度低字节
		xvalue  =  (xvalue_h<<8)|xvalue_l;				     	//获取MPU6050-X轴加速度
		mydelay_ms(100);

		//观测得知 开发板处于水平状态时，此时xvalue 处于 700到1000之间，故以此判断开发板是否水平放置
		if(xvalue > 700 && xvalue < 1000){
			//打印当前时间及开发板状态(每隔一分钟)
			NewMin = RTC.BCDMIN;
			if(OldMin != NewMin)
			{
				printf("20%x-%x-%x %x %x:%x:%x Normal\n",RTC.BCDYEAR, RTC.BCDMON, RTC.BCDWEEK, RTC.BCDDAY, RTC.BCDHOUR, RTC.BCDMIN, RTC.BCDSEC);
				OldMin = NewMin;
			}
			flag = 1;
		}else {
			//打印当前时间及开发板状态（每隔一秒钟）
			NewSec = RTC.BCDSEC;
			if(OldSec != NewSec)
			{
				printf("20%x-%x-%x %x %x:%x:%x Warning\n",RTC.BCDYEAR, RTC.BCDMON, RTC.BCDWEEK, RTC.BCDDAY, RTC.BCDHOUR, RTC.BCDMIN, RTC.BCDSEC);
				OldSec = NewSec;
			}
			//蜂鸣器响
			if(flag){
				PWM_init();                                             //初始化PWM
				PWM.TCON = PWM.TCON | 1;
				Delay(100000);
				PWM.TCON = PWM.TCON & (~(1));
				Delay(100000);
			}
		}
	}
	return 0;
}


